package com.yonyou.pmclouds.im.util;

import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.im.entity.IMExceptionCode;
import com.yonyou.pmclouds.im.entity.IMPushLogConst;
import com.yonyou.pmclouds.im.entity.IMPushLogVO;
import com.yonyou.pmclouds.im.service.IMPushLogService;
import com.yonyou.pmclouds.im.service.IMService;
import com.yonyou.pmclouds.task.entity.TaskVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
* @Description: IM推送执行类
*
* @author: zhangwce
* @Date: 2018/4/17
*/
@Slf4j
@Component
public class IMExecutor {

    private volatile ThreadPoolExecutor threadPoolExecutor = null;

    @Autowired
    private IMService imService;
    @Autowired
    private IMPushLogService imPushLogService;

    public void pushTodoMessage(final TaskVO[] taskVOs){

        threadPoolExecutor = getExecutorService();

        for (final TaskVO taskVO : taskVOs){
            threadPoolExecutor.execute(new TaskRunnable(taskVO));
        }

    }
    // 核心池大小
    private static int CORE_POOL_SIZE = 20;
    // 线程池最大容纳的线程数
    private static int MAXIMUM_POOL_SIZE = 50;
    private static long KEEP_ALIVE_TIME = 30L;

    /**
     * 获取线程池对象
     *
     * @return ExecutorService
     */
    private ThreadPoolExecutor getExecutorService() {
        if(threadPoolExecutor == null){
            synchronized (this){
                if(threadPoolExecutor == null) {
                    threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
                            MAXIMUM_POOL_SIZE,
                            KEEP_ALIVE_TIME,
                            TimeUnit.SECONDS,
                            new LinkedBlockingDeque<Runnable>(100),
                            new IMThreadFactory(),
                            new IMExecutor.IMRejectedExecutionHandler());
                }
            }
        }

        return threadPoolExecutor;
    }

    public class TaskRunnable implements Runnable{

        private TaskVO taskVO;

        TaskRunnable(TaskVO taskVO){
            this.taskVO =  taskVO;
        }

        @Override
        public void run() {
            try {
                log.info("开始推送代办消息：");
                imService.pushTodoMessage(taskVO,false);
            } catch (BusinessException e) {
                log.error("推送代办消息出错，具体信息为："+e.getMessage());
            }
        }

        public TaskVO getTaskVO() {
            return taskVO;
        }
    }

    /**
    * @Description: 线程池拒绝策略
     *
    * @author: zhangwce
    * @Date: 2018/4/22
    */
    public class IMRejectedExecutionHandler implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            if(r instanceof IMExecutor.TaskRunnable){
                TaskVO taskVO = ((IMExecutor.TaskRunnable)r).getTaskVO();
                String errMsg = "消息推送任务被拒绝执行";
                if(taskVO == null ){
                    log.error(errMsg);
                }else{
                    log.error(errMsg+",具体信息为："+taskVO.getSrc_bill_type()+"_"+taskVO.getPk_bill());
                    addPushLog(taskVO);
                }
            }

        }
        /**
        * @Description: 保存推送异常日志
        * @param: [taskVO]
        * @return: void
        * @author: zhangwce
        * @Date: 2018/4/22
        */
        private void addPushLog(TaskVO taskVO) {
            IMPushLogVO logVO = new IMPushLogVO();
            logVO.setBilltype(taskVO.getSrc_bill_type());
            logVO.setPkBill(taskVO.getPk_bill());
            logVO.setExcode(IMExceptionCode.RejectedExecution);
            logVO.setPkSender(taskVO.getPk_sender());
            logVO.setPkReceiver(taskVO.getPk_receiver());
            // IM推送异常信息，状态都标记为未处理，等以后有处理或补发机制再做处理
            logVO.setStatus(IMPushLogConst.UNSOLVED);

            try {
                imPushLogService.addIMPushLog(logVO);
            } catch (BusinessException e) {
                log.error("IM拒绝策略保存推送异常日志出错，具体信息为："+e.getMessage());
            }
        }
    }

}

